#pragma once
#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>
#include <string>
#include "messagebuffer.h"
#include "spinlock.h"

using boost::asio::ip::tcp;
using namespace std;
struct Header {
	unsigned int    length;
};

void xorfunc(std::string &nString);

class session
	: public std::enable_shared_from_this<session>
{
public:
	session(tcp::socket sock, string ip, short port):socket_(move(sock)), count_(0),ip_(ip),port_(port){}
	virtual tcp::socket connect(boost::asio::io_service& io_context_, string ip = "", short port = 0) = 0;
	void get_connect(tcp::socket socket)
	{
		SmartLock<SpinLock> smart_(lock_);
		socket_.close();
		socket_ = move(socket);
	}

	void start()
	{
		do_first();
	}
	//virtual bool reconnect() = 0;

protected:
	virtual void do_first()
	{
		do_read();
	};
	virtual string handle_cmd(const char* content, size_t length)
	{
		return string(content, length);
	}
	void do_read()
	{
		//cout << "begin socket:" << socket_.native_handle() << endl;
		auto self(shared_from_this());
		socket_.async_read_some(boost::asio::buffer(rbuf_.GetWritePointer(), rbuf_.GetRemainingSpace()),
			[this, self](boost::system::error_code ec, std::size_t length)
		{
			try
			{
				if ((length > 0))
				{
					rbuf_.WriteCompleted(length);
					while (handle_buffer(length))
					{
					}
					do_read();
				}
			}
			catch (std::exception& e)
			{
				std::cout << "read_some error:" << e.what() << std::endl;
			}
// 			else
// 			{
// 				if (ec.value() != 0 && count_ > 1)
// 				{
// 					cout << ec.value() << " " << ec.message() << " uid:" << Getuid() << endl;
// 					count_ = 0;
// 					//return;
// 				}
// 				count_++;
// 				get_connect(connect(*io_context));
// 				//cout << "die" << endl;
// 				do_first();
// 			}
		});
	}

	bool handle_buffer(std::size_t& length)
	{
		Header header;
		if (rbuf_.GetActiveSize() < sizeof(Header))
		{
			return false;
		}
		rbuf_ > header;
		if (header.length <= (rbuf_.GetActiveSize() - sizeof(Header)))
		{
			rbuf_ >> header;
			//cout << "length " << header.length << length << endl;
			//cout << "active " << rbuf_.GetActiveSize() << endl;
			string str = rbuf_.Read(header.length);
			//cout << rbuf_.GetActiveSize() << endl;
			xorfunc(str);
			//cout << str << endl;
			handle_cmd(str.data(), str.size());
			rbuf_.Normalize();
			//cout << "end" << endl;
			return true;
		}
		else
		{
			rbuf_.EnsureFreeSpace();
		}
		return false;
	}

	virtual int Getuid() = 0;

	void do_write(string content)
	{
		//auto self(shared_from_this());
		xorfunc(content);
		Header header;
		header.length = content.size();
		wbuf_ << header << content;
		//cout << "socket:" << socket_.native_handle() << " response: " << content << endl;
		again_write();
	}

	void again_write()
	{
		auto self(shared_from_this());
		boost::asio::async_write(socket_, boost::asio::buffer(wbuf_.GetReadPointer(), wbuf_.GetActiveSize()),
			[this, self](boost::system::error_code ec, std::size_t length)
		{
			SmartLock<SpinLock> smart_(lock_);
			if (!socket_.is_open())
			{
				cout << "socket is close!" << endl;
				return;
			}
			if (!ec)
			{
				wbuf_.ReadCompleted(length);
				//cout << "socket:" << socket_.native_handle() << " write is success " << endl;
				if (wbuf_.GetActiveSize() > 0)
				{
					again_write();
				}
				else
				{
					wbuf_.Normalize();
				}
			//	wbuf_.Normalize();
			}
			else
			{
				//cout << ec.value() << " " << ec.message() << " uid:" << Getuid() << endl;
				//socket_.close();
				socket_ = connect(*io_context, ip_, port_);
				do_first();
			}
		});
	}
	SpinLock lock_;
	boost::asio::io_service* io_context;
	string response;
	tcp::socket socket_;
// 	enum { max_length = 4096 };
// 	char data_[max_length];
	MessageBuffer rbuf_, wbuf_;
	int count_;
	string ip_;
	short port_;
};